﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace Twitter.Tests
{
	[TestFixture]
	public class BotProcessorFixture
	{
		[Test]
		public void ShouldMakeNewFriend()
		{
			MockGreeter greeter = new MockGreeter();
			MockProcessor processor = new MockProcessor(RunNextProcessor.Yes);

			IBotClient bot;
			IBotClient client;
			TestRun.CreateClients(out bot, out client);

			// just in case, leave the client
			bot.Friendship.Leave(TestRun.ClientScreenName);
			client.Friendship.Follow(TestRun.BotScreenName);

			BotProcessor proc = new BotProcessor(
                bot, greeter, null, processor);

			proc.Run();

			Assert.That(bot.Friendship.GetFriends().Contains(TestRun.ClientScreenName));
			Assert.That(greeter.NewFriend == TestRun.ClientScreenName);
		}

		[Test]
		public void ShouldSkipMakingFriends()
		{
			MockGreeter greeter = new MockGreeter();
			MockProcessor processor = new MockProcessor(RunNextProcessor.Yes);

			IBotClient bot;
			IBotClient client;
			TestRun.CreateClients(out bot, out client);

			// just in case, leave the client
			bot.Friendship.Leave(TestRun.ClientScreenName);
			client.Friendship.Follow(TestRun.BotScreenName);

			BotProcessor proc = new BotProcessor(
				bot, greeter, null, processor);

			proc.Run(false);

			Assert.IsFalse(bot.Friendship.GetFriends().Contains(TestRun.ClientScreenName));
			Assert.IsFalse(greeter.NewFriend == TestRun.ClientScreenName);
		}

		[Test]
		public void ShouldProcessMessage()
		{
			MockGreeter greeter = new MockGreeter();
			MockProcessor processor = new MockProcessor(RunNextProcessor.Yes);

			IBotClient bot;
			IBotClient client;
			TestRun.CreateClients(out bot, out client);

			// just in case, leave the client
			bot.Friendship.Leave(TestRun.ClientScreenName);
			client.Friendship.Follow(TestRun.BotScreenName);

			BotProcessor proc = new BotProcessor(
				bot, greeter, null, processor);

			proc.Run();

			client.Messages.Send(TestRun.BotScreenName, "foo");

			proc.Run();

			Assert.IsNotNull(processor.ProcessedMessage);
			Assert.AreEqual("foo", processor.ProcessedMessage.Text);
			Assert.AreEqual(TestRun.ClientScreenName, processor.ProcessedMessage.Sender.ScreenName);
		}

		[Test]
		public void ShouldSkipNextProcessorIfSpecified()
		{
			MockGreeter greeter = new MockGreeter();
			MockProcessor processor = new MockProcessor(RunNextProcessor.No);
			MockProcessor nextProcessor = new MockProcessor(RunNextProcessor.Yes);

			IBotClient bot;
			IBotClient client;
			TestRun.CreateClients(out bot, out client);

			// just in case, leave the client
			bot.Friendship.Leave(TestRun.ClientScreenName);
			client.Friendship.Follow(TestRun.BotScreenName);

			BotProcessor proc = new BotProcessor(
                bot, greeter, null, processor, nextProcessor);

			proc.Run();

			client.Messages.Send(TestRun.BotScreenName, "foo");

			proc.Run();

			Assert.IsNotNull(processor.ProcessedMessage);
			Assert.IsNull(nextProcessor.ProcessedMessage);
		}

		[Test]
		public void ShouldCallAllProcessorsUntilSpecified()
		{
			MockGreeter greeter = new MockGreeter();
			MockProcessor processor1 = new MockProcessor(RunNextProcessor.Yes);
			MockProcessor processor2 = new MockProcessor(RunNextProcessor.Yes);
			MockProcessor processor3 = new MockProcessor(RunNextProcessor.No);
			MockProcessor lastProcessor = new MockProcessor(RunNextProcessor.Yes);

			IBotClient bot;
			IBotClient client;
			TestRun.CreateClients(out bot, out client);

			// just in case, leave the client
			bot.Friendship.Leave(TestRun.ClientScreenName);
			client.Friendship.Follow(TestRun.BotScreenName);

			BotProcessor proc = new BotProcessor(
                bot, greeter, null, processor1, processor2, processor3, lastProcessor);

			proc.Run();

			client.Messages.Send(TestRun.BotScreenName, "foo");

			proc.Run();

			Assert.IsNotNull(processor1.ProcessedMessage);
			Assert.IsNotNull(processor2.ProcessedMessage);
			Assert.IsNotNull(processor3.ProcessedMessage);
			Assert.IsNull(lastProcessor.ProcessedMessage);
		}

        [Test]
        public void ShouldNotStopIfExceptionIsThrownFromProcessor()
        {
            MockGreeter greeter = new MockGreeter();
            MockProcessor processor1 = new MockProcessor(RunNextProcessor.Yes);
            MockProcessor processor2 = new MockProcessor(new Exception());
            MockProcessor processor3 = new MockProcessor(RunNextProcessor.No);
            MockProcessor lastProcessor = new MockProcessor(RunNextProcessor.Yes);

            IBotClient bot;
            IBotClient client;
            TestRun.CreateClients(out bot, out client);

            // just in case, leave the client
            bot.Friendship.Leave(TestRun.ClientScreenName);
            client.Friendship.Follow(TestRun.BotScreenName);

            BotProcessor proc = new BotProcessor(
                bot, greeter, null, processor1, processor2, processor3, lastProcessor);

            proc.Run();

            client.Messages.Send(TestRun.BotScreenName, "foo");

            proc.Run();

            Assert.IsNotNull(processor1.ProcessedMessage);
            Assert.IsNotNull(processor2.ProcessedMessage);
            Assert.IsNotNull(processor3.ProcessedMessage);
            Assert.IsNull(lastProcessor.ProcessedMessage);
        }

		class MockGreeter : IGreeter
		{
			public string NewFriend;

			public void Greet(IBotClient client, string newFriend)
			{
				this.NewFriend = newFriend;
			}
		}

		class MockProcessor : IMessageProcessor
		{
			public Message ProcessedMessage;

			private RunNextProcessor runDefault;
            private Exception toThrow;

			public MockProcessor(RunNextProcessor runDefault)
			{
				this.runDefault = runDefault;
			}

            public MockProcessor(Exception toThrow)
            {
                this.toThrow = toThrow;
            }

            public bool CanProcess(Message message)
            {
                return true;
            }

			public RunNextProcessor Process(IBotClient client, Message message)
			{
				this.ProcessedMessage = message;

                // throw expected exception
                if (toThrow != null)
                    throw toThrow;

				client.Messages.Delete(message.Id);
				return runDefault;
			}
		}
	}
}
